// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
//
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.core.scripts;

import static org.dengues.core.runprocess.JavaProcessorUtil.JAVA_DENGUES_PKG;
import static org.dengues.core.runprocess.JavaProcessorUtil.JAVA_PROJ_INTERNAL;
import static org.dengues.core.runprocess.JavaProcessorUtil.JAVA_PROJ_SRC;
import static org.dengues.core.runprocess.JavaProcessorUtil.JAVA_SRC_DENGUES_INTERNAL;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.dengues.core.DenguesCorePlugin;
import org.dengues.core.metadata.types.MetadataTypesManager;
import org.dengues.core.resource.WarehouseResourceFactory;
import org.dengues.core.runprocess.IJavaETLProcessor;
import org.dengues.core.warehouse.ENodeCategoryName;
import org.dengues.model.warehouse.Storage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.ui.text.IColorManager;
import org.eclipse.jdt.ui.text.IJavaPartitions;
import org.eclipse.jdt.ui.text.JavaTextTools;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.source.AnnotationModel;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.OverviewRuler;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.internal.editors.text.EditorsPlugin;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
import org.eclipse.ui.texteditor.DefaultRangeIndicator;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;

/**
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf Qiang.Zhang.Adolf@gmail.com 2008-6-3 qiang.zhang $
 * 
 */
public class JavaScriptsSourceViewer extends JavaReconcilerViewer {

    private String filename;

    public static final String VIEWER_CLASS_NAME = "DenguesJavaSourceViewer";

    private static int currentId = 0;

    private static HashMap<String, List<ScriptVariable>> classesToGenerate;

    private ICompilationUnit compilationUnit;

    private MarkerAnnotationPreferences fAnnotationPreferences;

    /**
     * Qiang.Zhang.Adolf@gmail.com DenguesJavaSourceViewer constructor comment.
     * 
     * @param parent
     * @param verticalRuler
     * @param overviewRuler
     * @param showAnnotationsOverview
     * @param styles
     * @param annotationAccess
     * @param sharedColors
     * @param checkCode
     * @param document
     */
    public JavaScriptsSourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler,
            boolean showAnnotationsOverview, int styles, IAnnotationAccess annotationAccess, ISharedTextColors sharedColors,
            boolean checkCode, IDocument document) {
        super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, styles, annotationAccess, sharedColors, checkCode,
                document);
        IPath packagePath = new Path(JAVA_SRC_DENGUES_INTERNAL);
        filename = VIEWER_CLASS_NAME + currentId++ + ".java";
        IPackageFragment packageFragment;
        try {
            IJavaETLProcessor javaProcessor = DenguesCorePlugin.getDefault().getDesignerCoreService().getJavaProcessor();
            packageFragment = javaProcessor.getJavaProject().findPackageFragment(packagePath);
            compilationUnit = packageFragment.createCompilationUnit(filename, document.get(), false, new NullProgressMonitor());
        } catch (Exception e) {
            e.printStackTrace();
        }
        updateContents();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.viewer.ReconcilerViewer#initializeModel()
     */
    @Override
    protected void initializeModel() {
        getSourceViewerDecorationSupport().install(JavaPlugin.getDefault().getCombinedPreferenceStore());
        this.setRangeIndicator(new DefaultRangeIndicator());

        IAnnotationModel model;
        if (checkCode) {
            IDocumentProvider provider = JavaPlugin.getDefault().getCompilationUnitDocumentProvider();
            IEditorInput ei = new FileEditorInput(file);
            try {
                provider.connect(ei);
            } catch (CoreException e) {
                e.printStackTrace();
            }
            model = provider.getAnnotationModel(ei);
        } else {
            model = new AnnotationModel();
        }
        IDocument document = getDocument();
        model.connect(document);

        if (document != null) {
            setDocument(document, model);
            showAnnotations(model != null && checkCode);
        }
        super.initializeModel();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.viewer.ReconcilerViewer#installViewerConfiguration()
     */
    @Override
    protected void installViewerConfiguration() {
        JavaTextTools tools = JavaPlugin.getDefault().getJavaTextTools();
        tools.setupJavaDocumentPartitioner(getDocument(), IJavaPartitions.JAVA_PARTITIONING);
        IPreferenceStore store = JavaPlugin.getDefault().getCombinedPreferenceStore();
        configure(new JavaScriptsViewerConfiguration((IColorManager) sharedColors, store, this));
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.viewer.ReconcilerViewer#updateContents()
     */
    @Override
    public void updateContents() {
        IPath filePath = new Path(File.separator + JAVA_PROJ_SRC + File.separator + JAVA_DENGUES_PKG + File.separator
                + JAVA_PROJ_INTERNAL + File.separator + filename);
        if (getDocument() == null) {
            return;
        }
        InputStream codeStream = new ByteArrayInputStream(getDocument().get().getBytes());
        try {
            if (file == null) {
                file = DenguesCorePlugin.getDefault().getDesignerCoreService().getJavaProcessor().getJavaProject().getProject()
                        .getFile(filePath);
                file.setContents(codeStream, true, false, null);
                initializeModel();
            } else {
                // only happen sometimes with a "fast click" (file don't exist yet).
                if (!file.exists()) {
                    return;
                }
                file.setContents(codeStream, true, false, null);
            }
        } catch (CoreException e) {
            e.printStackTrace();
        }
    }

    /**
     * Getter for compilationUnit.
     * 
     * @return the compilationUnit
     */
    public ICompilationUnit getCompilationUnit() {
        return this.compilationUnit;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "createViewerWithVariables".
     * 
     * @param composite
     * @param styles
     * @param dataBean
     * @return
     */
    public static JavaReconcilerViewer createViewerWithScripts(Composite composite, int styles, ScriptDataBean dataBean) {
        IDocument document = new Document();
        StringBuffer buff = new StringBuffer();
        buff.append("\npackage dengues.internal;\n\n");
        buff.append(getImports());
        buff.append("public class " + VIEWER_CLASS_NAME + currentId + " {\n");
        buff.append("\tprivate static java.util.Properties context = new java.util.Properties();\n");
        buff
                .append("\tprivate static final java.util.Map<String, Object> globalMap = new java.util.HashMap<String, Object>();\n");

        buff.append(parseVariables(dataBean.getVariables()));
        buff.append("\tpublic " + dataBean.getDataType() + " myFunction(){\n");
        buff.append("\t\treturn \n");

        int length = buff.toString().length();
        String defaultValue = "";
        buff.append(defaultValue + "\n;\t\n}\n}");

        document.set(buff.toString());

        return initializeViewer(composite, styles, true, document, length);
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "parseVariables".
     * 
     * @param variables
     * @return
     */
    private static String parseVariables(List<ScriptVariable> variables) {
        StringBuffer buff = new StringBuffer();
        classesToGenerate = new HashMap<String, List<ScriptVariable>>();

        if (variables != null) {
            for (ScriptVariable var : variables) {
                if (var.getName() != null && var.getTypeId() != null) {
                    if (var.getName().contains(".")) {
                        // class
                        StringTokenizer token = new StringTokenizer(var.getName(), ".");
                        String className = token.nextToken();
                        String newVarName = token.nextToken();
                        ScriptVariable newVar = new ScriptVariable(newVarName, var.getTypeId(), var.getNodeId());
                        if (!classesToGenerate.containsKey(className)) {
                            classesToGenerate.put(className, new ArrayList<ScriptVariable>());
                        }
                        classesToGenerate.get(className).add(newVar);
                    } else {
                        String typeToGenerate = MetadataTypesManager.getJavaTypeName(var.getTypeId());
                        buff.append("\tprivate " + typeToGenerate + " " + var.getName() + ";\\n");
                    }
                }
            }
            for (String className : classesToGenerate.keySet()) {
                buff.append("\tprivate class " + className + "Struct {\n");
                for (ScriptVariable var : classesToGenerate.get(className)) {
                    String typeToGenerate = MetadataTypesManager.getJavaTypeName(var.getTypeId());
                    buff.append("\t\tprivate " + typeToGenerate + " " + var.getName() + ";\n");
                }
                buff.append("\t};\n");
                buff.append("\tprivate " + className + "Struct " + className + "= new " + className + "Struct();\n");
            }
        }
        buff.append("\t" + VIEWER_CLASS_NAME + currentId + "() {\n");
        buff.append("\t\tglobalMap.put(\"NULL\", null);\n");
        if (variables != null) {
            for (String className : classesToGenerate.keySet()) {
                for (ScriptVariable var : classesToGenerate.get(className)) {
                    buff.append("\t\t" + className + "." + var.getName() + " =  " + className + "." + var.getName() + ";\n");
                }
            }
        }
        buff.append("\t}\n");

        return buff.toString();
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getImports".
     * 
     * @return
     */
    private static String getImports() {
        String imports = "";
        List<Object> allScripts = WarehouseResourceFactory.getAllObjectsFromEMF(ENodeCategoryName.SCRIPTS);
        for (Object object : allScripts) {
            Storage storage = (Storage) object;
            imports += "import dengues.customize." + storage.getName() + ";\n";
        }

        imports += "import dengues.system.*;\n";

        imports += "import java.text.ParseException;\n";
        imports += "import java.text.SimpleDateFormat;\n";
        imports += "import java.util.Date;\n";
        imports += "import java.util.List;\n";
        imports += "import java.math.BigDecimal;\n";
        return imports;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.dengues.core.scripts.JavaReconcilerViewer#initializeViewer(org.eclipse.jface.text.IDocument)
     */
    private static JavaReconcilerViewer initializeViewer(Composite composite, int styles, boolean checkCode, IDocument document,
            int visibleOffset) {
        IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess();
        ISharedTextColors sharedColors = JavaPlugin.getDefault().getJavaTextTools().getColorManager();
        IOverviewRuler overviewRuler = null;
        IVerticalRuler verticalRuler = null;
        if (checkCode) {
            overviewRuler = new OverviewRuler(annotationAccess, 12, sharedColors);
            Iterator e = EditorsPlugin.getDefault().getMarkerAnnotationPreferences().getAnnotationPreferences().iterator();
            while (e.hasNext()) {
                AnnotationPreference preference = (AnnotationPreference) e.next();
                if (preference.contributesToHeader()) {
                    overviewRuler.addHeaderAnnotationType(preference.getAnnotationType());
                }
            }
        }
        verticalRuler = new CompositeRuler(12);
        JavaReconcilerViewer viewer = new JavaScriptsSourceViewer(composite, verticalRuler, overviewRuler, checkCode, styles,
                annotationAccess, sharedColors, checkCode, document);

        if (visibleOffset != -1) {
            viewer.setVisibleRegion(visibleOffset, 0);
        }

        return viewer;
    }

    /**
     * Getter for classesToGenerate.
     * 
     * @return the classesToGenerate
     */
    public static HashMap<String, List<ScriptVariable>> getClassesToGenerate() {
        return classesToGenerate;
    }

}
